# Copyright 2024 Henrique Paone
#
# This file is part of Tanki.
#
# Tanki is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
# 
# Tanki is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License along with
# Tanki. If not, see <https://www.gnu.org/licenses/>.

require 'date'
require_relative 'tui/tui.rb'
require_relative 'error.rb'

class Card
  include Tui
  include Error
  
  attr_reader :title, :prompt, :answer
  attr_writer :stage, :last_review

  Regexes = {
    # Everything after a # is ignore, \# is allowed
    :comment => /(?<!\\)#.*/,
    
    # The stage is an arbitrary number
    :stage => /^Stage:\s*\K\d+/,
    
    # Gets the date in the format year-month-day
    :last_review => /^LastReview:\s*\K\d{4}-\d{1,2}-\d{1,2}/,
    
    # Matches everything between two %. To get the answer, we apply this match
    # then check in the $' variable and to get the title we get until the first \n
    :prompt => /%\K(.*)\K%/m
  }

  def method_missing(m, *args, &block)
    show(false, *args) if m == :show_prompt
    show(true, *args) if m == :show_answer
  end
  
  def initialize(file)
    @file = File.new(file)
    @text = @file.read
    parse
  end

  def show(show_answer, amt)
    lines, cols = *IO.console.winsize
    cstart = lstart = margin = 5
    lend = lines - margin
    cend = cols - margin
    lbeg = cstart + 1
    
    IO.console.clear_screen
    draw_box(cstart, lstart, lend, cend)
    lstart += 1
    
    move_cursor(lstart, lbeg)
    print @file.path
    card_str = 'cards'
    card_str = 'card' if amt == 1
    print_line_before("#{ amt } #{ card_str } pending", lstart, cend - 1)
    lstart += 1
    
    draw_vline_inbox(cstart, cend, lstart)
    lstart += 3

    lstart = print_centered(@prompt, lstart, lbeg, cstart, cend - 1)
    
    lstart += ((lend - lstart) / 2) - 2
    print_centered(@answer, lstart, lbeg, cstart, cend - 1) if show_answer

    options = {
      right: 'r: Right',
      wrong: 'w: Wrong',
      quit: 'q: Quit',
      enter: '--ENTER--'
    }
    
    draw_vline_inbox(cstart, cend, lend - 2)
    move_cursor(lend - 1, lbeg)
    unless show_answer
      # Before asking to show the answer
      print options[:enter].center(cend - cstart - 1)
    else
      # After asking to show the answer
      print options[:right].center(cend - cstart - 1)
      move_cursor(lend - 1, lbeg)
      print options[:wrong]
    end
    print_line_before(options[:quit], lend - 1, cend - 1)
    move_cursor(lines, 1)
  end

  def parse
    text = @text.gsub(Regexes[:comment], '')
    
    last_review = text.scan(Regexes[:last_review]).first
    stage = text.scan(Regexes[:stage]).first
    text.gsub!("Stage: #{ stage }", '')
    text.gsub!("LastReview: #{ last_review }", '')
    # Note: prompt must be the last scan so we can fetch the answer using $'
    prompt = text.scan(Regexes[:prompt]).first

    raise ParsingError, Err_msgs[:parse ] % { file: @file.path } if prompt.nil?
    
    @stage = stage.nil? ? 0 : stage.to_i
    begin
      @last_review = last_review.nil? ? Date.today : Date.strptime(last_review, '%Y-%m-%d')
      rescue Date::Error
        raise BadValue, Err_msgs[:bad_date] % { file: @file }
    end
    @prompt = prompt.first
    @answer = $'
  end

  def write
    text = @text.split("\n")
    stage_idx = text.index do |line|
      line.match?(Regexes[:stage])
    end
    last_review_idx = text.index do |line|
      line.match?(Regexes[:last_review])
    end
    if stage_idx.nil?
      text << ""
      text << "# Values for internal tanki use, dont touch them unless you know what you're doing"
      text << "Stage: #{ @stage }"
    else
      text[stage_idx] = "Stage: #{ @stage }"
    end
    if last_review_idx.nil?
      text << "LastReview: #{ @last_review }"
    else
      text[last_review_idx] = "LastReview: #{ @last_review }"
    end
    File.open(@file.path, 'w').write(text.join("\n") << "\n")
    @file.close
  end
  
  def pending?
    Date.today >= @last_review + stage_dif
  end

  def check_syntax
  end

  def reset
    @stage = 0
  end

  def incr
    @stage += 1
  end

  def stage_dif
    dif = count = 0
    skip = false
    while count != @stage
      unless skip
        dif += 1
        skip = true
      else
        skip = false
      end
      count += 1
    end
    dif
  end
end
